package DataFile;

import java.nio.ByteBuffer;

import ClassData.ClassData;
/*
import ClassData.ClassDataList;
import ClassData.ConstantPoolField_Info;
import ClassData.ConstantPoolUtf8_Info;
*/

/**
 * @note changed by Dominik Ollesz
 */
public class Structure_ClassData extends Structure_Template {

	
	//	Static Fields
	//  
	
	/*
	private short staticAreaSize;
	private short dynamicAreaSize;
	private short inheritedStaticAreaSize;
	private short inheritedDynamicAreaSize;
		*/
	
	private Structure_FieldTable fieldTable;	
	private Structure_MethodTable methodTable;
	private Structure_CP_Table constantPoolTable;
	private Structure_ExceptionHandlerTable exceptionHandlerTable;
	private Structure_ExceptionsTable exceptionsTable;
	
	ClassData myClassData;
	
	short staticDatahandle;


	public Structure_ClassData(ClassData myClassData) {
		
		System.out.println(myClassData.getClassName());
		
		this.myClassData = myClassData; 
	
		constantPoolTable= new Structure_CP_Table(myClassData.getConstantPool());
		fieldTable = new Structure_FieldTable(myClassData.getFields());
		methodTable = new Structure_MethodTable(myClassData.getMethods());
		
		calculateFieldsOffset();
		// Calculate Size
		
	}

	protected int getClassMemorySize() {
		int size;
		size=getMemorySize();
		
		size+=constantPoolTable.getMemorySize();
		size+=constantPoolTable.getTableDataMemorySize();
		
		size+=fieldTable.getMemorySize();
		size+=fieldTable.getTableDataMemorySize();
		
		return(size);
		
	}

	protected int getMemorySize() {
		int size;
		
		size =ADRESS_WIDTH; // constant pool table address
		size+=ADRESS_WIDTH; // Field table address
		size+=ADRESS_WIDTH; // Method table address
	
		size+=SHORT_WIDTH; // Static data size
		size+=SHORT_WIDTH; // Dynamic data size
		size+=SHORT_WIDTH; // Static Data Handle;
		size+=SHORT_WIDTH; // unused
		
		return(size);	
	}

	protected String getMemoryMapAsString() {
		String returnString;	
		int counter;
		
		returnString="\nClass : "+myClassData.getClassName()+"\n";
		returnString+=" : Class adress :"+super.getMemoryMapAsString()+"\n";	
		returnString+=" : Class data size - "+getClassMemorySize()+"\n";
		returnString+=" : Class structure size - "+getMemorySize()+"\n";
		
		returnString+="\nConstant Pool Table\n";
		for (counter = 1; counter<=constantPoolTable.size();counter++) {
			returnString+=String.format("%03d", counter) + " " +constantPoolTable.get(counter).getMemoryMapAsString();
		}
		
		returnString+=fieldTable.getMemoryMapAsString();
		
		for (counter = 0; counter<fieldTable.size();counter++) {
			returnString+=String.format("%03d",counter) + " " + fieldTable.get(counter).getMemoryMapAsString();
		}
		
		returnString+=methodTable.getMemoryMapAsString();
		for (counter = 0; counter<methodTable.size();counter++) {
			returnString+=String.format("%03d", counter) + " " + methodTable.get(counter).getMemoryMapAsString();
		}
		
		returnString+=exceptionHandlerTable.getMemoryMapAsString();
		for (counter = 0; counter<exceptionHandlerTable.size();counter++) {
			returnString+=String.format("%03d", counter) + " " 
				+ exceptionHandlerTable.get(counter).getMemoryMapAsString(constantPoolTable);
		}
		
		returnString+=exceptionsTable.getMemoryMapAsString(constantPoolTable);
		
		return(returnString);
	}

	public Structure_CP_Table getConstantPoolTable() {
		return(constantPoolTable);
	}


	protected int setBaseAdress(int newBaseAdress) {
		int counter;	
		int nextAdress;
	
		while ((newBaseAdress%4)!=0) {
			newBaseAdress++;
		}
	
		// constant pool
		nextAdress=super.setBaseAddress(newBaseAdress);
		
		nextAdress=constantPoolTable.setBaseAddress(nextAdress);
		for (counter = 0; counter<constantPoolTable.size();counter++) {
			nextAdress=constantPoolTable.get(counter+1).setBaseAddress(nextAdress);
		}
		
		while ((nextAdress % 4)>0) {
			nextAdress++;
		}
		
		// field table
		nextAdress=fieldTable.setBaseAddress(nextAdress);
		
		for (counter = 0; counter<fieldTable.size();counter++) {
			nextAdress=fieldTable.get(counter).setBaseAddress(nextAdress);
		}
		
		// method table
		nextAdress=methodTable.setBaseAddress(nextAdress);
		
		for (counter = 0; counter<methodTable.size();counter++) {
			nextAdress=methodTable.get(counter).setBaseAddress(nextAdress);
		}
		
		// Pre-Calculation for: 
		//  - exception handler table
		//  - exceptions table
		
			// 1. exception handler table
			exceptionHandlerTable = new Structure_ExceptionHandlerTable();
		
			// refresh exception handler table address for all methods
			for (counter = 0; counter<methodTable.size();counter++) {
				methodTable.get(counter).setExceptionHandlerTable(exceptionHandlerTable);
			} nextAdress = exceptionHandlerTable.setBaseAddress(nextAdress);
		
			// 2. exceptions table
			exceptionsTable = new Structure_ExceptionsTable();
		
			// refresh exceptions table address for all methods
			for (counter = 0; counter<methodTable.size();counter++) {
				methodTable.get(counter).setExceptionsTable(exceptionsTable);
			} nextAdress = exceptionsTable.setBaseAddress(nextAdress);
		
		return(nextAdress);
	}

	public int getBaseAdress() {
		return(baseAdress);
	}

	public String getClassName() {
		return(myClassData.getClassName());
	}

	public String getSuperClassName() {
		return(myClassData.getSuperClassName());
	}

	public Structure_FieldData getField(String fieldName) {
		// int counter;
		int fieldIndex;
		
		Structure_FieldData myField;
		
		for (fieldIndex=0;fieldIndex<fieldTable.size();fieldIndex++) {
			myField=fieldTable.get(fieldIndex);
			if (myField.getFieldName().equals(fieldName)) {
				System.out.println("Find : "+myField.getFieldName()
						+" in Class "+getClassName());
				return (myField);
			}
		}
	
		return(null);
	}

	public String getFieldName(Structure_CP_FieldInfo fieldInfo) {
		int nameIndex;
		Structure_Template myInfo;
		Structure_CP_NameInfo myNameInfo;
		Structure_CP_UTF8Info myUtf8Info;
		int fieldNameIndex;
		
		nameIndex=fieldInfo.getFieldDescriptorIndex();
		
		myInfo=constantPoolTable.get(nameIndex);
		
		if (constantPoolTable.get(nameIndex).getClass().getName().equals(Structure_CP_NameInfo.class.getName())) {
			myNameInfo=(Structure_CP_NameInfo)constantPoolTable.get(nameIndex);
			fieldNameIndex=myNameInfo.getNameIndex();
			
			if (constantPoolTable.get(fieldNameIndex).getClass().getName().equals(Structure_CP_UTF8Info.class.getName())) {
				myUtf8Info=(Structure_CP_UTF8Info)constantPoolTable.get(fieldNameIndex);
				return(myUtf8Info.getString());
			}		
		}
		
		return("");
	}

	public Structure_MethodData getMethod(String methodName, String parameters) {
		//int counter;
		int fieldIndex;
		
		Structure_MethodData myMethod;
		
		for (fieldIndex=0;fieldIndex<methodTable.size();fieldIndex++) {
			myMethod=methodTable.get(fieldIndex);
			
			if ((myMethod.getMethodName().equals(methodName)) && ((myMethod.getParametersAsString().equals(parameters)))) {
				System.out.println("Find : "+myMethod.getMethodName()
						+" "+myMethod.getParametersAsString()
						+ "  in Class "+getClassName());
				return (myMethod);
			}
		}
		
		return(null);
	}

	public String getMethodName(Structure_CP_MethodInfo methodInfo) {
		int nameIndex;
		Structure_Template myInfo;
		Structure_CP_NameInfo myNameInfo;
		Structure_CP_UTF8Info myUtf8Info;
		int methodNameIndex;
		
		nameIndex=methodInfo.getDescriptorIndex();
		
		myInfo=constantPoolTable.get(nameIndex);
		
		if (constantPoolTable.get(nameIndex).getClass().getName().equals(Structure_CP_NameInfo.class.getName())) {
			myNameInfo=(Structure_CP_NameInfo)constantPoolTable.get(nameIndex);
			methodNameIndex=myNameInfo.getNameIndex();
			
			if (constantPoolTable.get(methodNameIndex).getClass().getName().equals(Structure_CP_UTF8Info.class.getName())) {
				myUtf8Info=(Structure_CP_UTF8Info)constantPoolTable.get(methodNameIndex);
				return(myUtf8Info.getString());
			}
		}
		
		return("");
	}

	public void calculateFieldsOffset() {
		short staticOffset;
		short instanceOffset;
		int counter;
		
		staticOffset=(short)myClassData.getInheritedStaticDataSize();
		instanceOffset=(short)myClassData.getIinheritedInstanceDataSize();
		Structure_FieldData myFieldData;
		
		for (counter = 0; counter<fieldTable.size();counter++) {
			myFieldData=fieldTable.get(counter);
			if (myFieldData.isStatic()) {
				staticOffset=myFieldData.setOffset(staticOffset);
			} else {
				instanceOffset=myFieldData.setOffset(instanceOffset);
			}
		}
	}


	public void setStaticDataHandle(short handle) {
		staticDatahandle=handle;	
	}

	/**
	 *  writes data into memory file
	 *  
	 *  @author Tavin
	 *  
	 *  @param fileData buffer for data 
	 */
	public void writeData(ByteBuffer fileData) {
		fileData.position(baseAdress-DataFileCreator.BASE_MEMORY_ADRESS);
		
		fileData.putInt(constantPoolTable.getBaseAddress());              //   4 Bytes
		fileData.putInt(fieldTable.getBaseAddress());                     //   4 Bytes
		fileData.putInt(methodTable.getBaseAddress());                    //   4 Bytes
		
		fileData.putShort((short)myClassData.getTotalStaticDataSize());   //   2 Bytes
		fileData.putShort((short)myClassData.getTotalInstanceDataSize()); //   2 Bytes
		fileData.putShort((short)staticDatahandle);                       //   2 Bytes
		fileData.putShort((short)0);                                      //   2 Bytes
		                                                                  // ----------
		                                                                  //  24 Bytes
		
		constantPoolTable.writeData(fileData);
		fieldTable.writeData(fileData);
		methodTable.writeData(fileData);
		
		exceptionHandlerTable.writeData(fileData);
		exceptionsTable.writeData(fileData);
		
		//for (counter=0;counter<classTable.size();counter++)
		//{
		//	fileData.putInt(classTable.get(counter).getBaseAdress());
	    //}
	}
	
}
